package database

import (
	"gitee.com/amoyx/cloud-provider/pkg/cloud/tencent/models"
	qcdb "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cdb/v20170320"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	"strconv"
	"strings"
)

type CDBGreeter interface {
	CDB(region string) CDBInterface
}

type CDBInterface interface {
	CreatePrepaidInstance(args models.MySQLRequest) (result models.CreateResponseParams, err error)
	CreatePostpaidInstance(args models.MySQLRequest) (result models.CreateResponseParams, err error)
	Get(id string) (result models.MySQLResponse, err error)
	List() (list []models.MySQLResponse, err error)
	ListByName(name string) (list []models.MySQLResponse, err error)
	Delete(id string) (result models.CreateResponseParams, err error)
	IsolateDBInstance(id string) (result models.CreateResponseParams, err error)
	QueryTaskList(id string) (list []models.TaskDetail, err error)
	QueryAsyncRequestDetail(asyncId string) (result models.AsyncRequestInfoResponse, err error)
	ResetDBPassword(id string, newPasswd string, user string, host string) (result models.CreateResponseParams, err error)
	ListDBUsers(id string) (list []models.DBAccountInfo, err error)
	CreateDBUser(id string, args models.DBUserRequest) (err error)
	DeleteDBUer(id string, user string, host string) (err error)
	DisableWanAddr(id string) (result models.CreateResponseParams, err error)
	EnableWanAddr(id string) (result models.CreateResponseParams, err error)
	UpgradeConfigInfo(id string, args models.MySQLRequest) (result models.CreateResponseParams, err error)
	QueryPrepaidPrice(args models.MySQLRequest) (price models.ItemPrice, err error)
	QueryPostpaidPrice(args models.MySQLRequest) (price models.ItemPrice, err error)
}

type cdb struct {
	region string
	db     *Database
	client *qcdb.Client
}

func newCDB(db *Database, region string) CDBInterface {
	c := &cdb{
		region: region,
		db:     db,
	}
	cpf := profile.NewClientProfile()
	cpf.HttpProfile.Endpoint = string(CDBEndPoint)
	client, _ := qcdb.NewClient(c.db.credential, c.region, cpf)
	c.client = client
	return c
}

// CreatePrepaidInstance 创建预付费实例, 包年包月
func (t *cdb) CreatePrepaidInstance(args models.MySQLRequest) (result models.CreateResponseParams, err error) {
	request := qcdb.NewCreateDBInstanceRequest()
	request.Memory = common.Int64Ptr(args.Memory)
	request.Volume = common.Int64Ptr(args.Volume)
	request.Period = common.Int64Ptr(args.Period)
	request.GoodsNum = common.Int64Ptr(args.GoodsNum)
	request.Zone = common.StringPtr(args.Zone)
	request.UniqVpcId = common.StringPtr(args.VpcId)
	request.UniqSubnetId = common.StringPtr(args.SubnetId)
	request.Port = common.Int64Ptr(args.Port)
	request.InstanceRole = common.StringPtr(args.InstanceRole)
	if args.InstanceRole == "dr" || args.InstanceRole == "ro" {
		request.MasterInstanceId = common.StringPtr(args.MasterInstanceId)

	}
	request.EngineVersion = common.StringPtr(args.EngineVersion)
	request.Password = common.StringPtr(args.Password)
	request.AutoRenewFlag = common.Int64Ptr(1)
	request.SecurityGroup = common.StringPtrs(args.SecurityGroup)
	request.InstanceName = common.StringPtr(args.InstanceName)
	request.DeviceType = common.StringPtr(args.DeviceType)
	request.Cpu = common.Int64Ptr(args.Cpu)
	request.EngineType = common.StringPtr("InnoDB")
	//request.ProtectMode = common.Int64Ptr(args.ProtectMode)
	//request.DeployMode = common.Int64Ptr(args.DeployMode)
	//request.SlaveZone = common.StringPtr(args.SlaveZone)
	//request.MasterRegion = common.StringPtr("ap-shanghai")
	//request.InstanceNodes = common.Int64Ptr(1)
	response, err := t.client.CreateDBInstance(request)
	if err != nil {
		return
	}
	res := response.Response
	result.RequestId = *res.RequestId
	for _, id := range res.DealIds {
		result.TaskId = append(result.TaskId, *id)
	}
	for _, id := range res.InstanceIds {
		result.InstanceSet = append(result.InstanceSet, *id)
	}
	return
}

// CreatePostpaidInstance 创建后付费实例, 按量计费
func (t *cdb) CreatePostpaidInstance(args models.MySQLRequest) (result models.CreateResponseParams, err error) {
	request := qcdb.NewCreateDBInstanceHourRequest()
	request.Memory = common.Int64Ptr(args.Memory)
	request.Volume = common.Int64Ptr(args.Volume)
	request.GoodsNum = common.Int64Ptr(args.GoodsNum)
	request.Zone = common.StringPtr(args.Zone)
	request.UniqVpcId = common.StringPtr(args.VpcId)
	request.UniqSubnetId = common.StringPtr(args.SubnetId)
	request.Port = common.Int64Ptr(args.Port)
	request.InstanceRole = common.StringPtr(args.InstanceRole)
	if args.InstanceRole == "dr" || args.InstanceRole == "ro" {
		request.MasterInstanceId = common.StringPtr(args.MasterInstanceId)

	}
	request.EngineVersion = common.StringPtr(args.EngineVersion)
	request.Password = common.StringPtr(args.Password)
	request.AutoRenewFlag = common.Int64Ptr(1)
	request.SecurityGroup = common.StringPtrs(args.SecurityGroup)
	request.InstanceName = common.StringPtr(args.InstanceName)
	request.DeviceType = common.StringPtr(args.DeviceType)
	request.Cpu = common.Int64Ptr(args.Cpu)
	request.EngineType = common.StringPtr("InnoDB")
	response, err := t.client.CreateDBInstanceHour(request)
	if err != nil {
		return
	}
	res := response.Response
	result.RequestId = *res.RequestId
	for _, id := range res.DealIds {
		result.TaskId = append(result.TaskId, *id)
	}
	for _, id := range res.InstanceIds {
		result.InstanceSet = append(result.InstanceSet, *id)
	}
	return
}

func (t *cdb) Get(id string) (result models.MySQLResponse, err error) {
	list, err := t.query(map[string]string{"id": id})
	if len(list) > 0 {
		result = list[0]
	}
	return
}

func (t *cdb) List() (list []models.MySQLResponse, err error) {
	return t.query(map[string]string{})
}

func (t *cdb) ListByName(name string) (list []models.MySQLResponse, err error) {
	return t.query(map[string]string{"name": name})
}

// Delete 删除/下线隔离后的数据库实例
func (t *cdb) Delete(id string) (result models.CreateResponseParams, err error) {
	request := qcdb.NewOfflineIsolatedInstancesRequest()
	request.InstanceIds = common.StringPtrs([]string{id})
	response, err := t.client.OfflineIsolatedInstances(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	return
}

// IsolateDBInstance 隔离数据库实例
func (t *cdb) IsolateDBInstance(id string) (result models.CreateResponseParams, err error) {
	request := qcdb.NewIsolateDBInstanceRequest()
	request.InstanceId = common.StringPtr(id)
	response, err := t.client.IsolateDBInstance(request)
	result.RequestId = *response.Response.RequestId
	result.TaskId = append(result.TaskId, *response.Response.AsyncRequestId)
	return
}

// QueryTaskList 查询数据库任务清单
func (t *cdb) QueryTaskList(id string) (list []models.TaskDetail, err error) {
	request := qcdb.NewDescribeTasksRequest()
	request.InstanceId = common.StringPtr(id)
	response, err := t.client.DescribeTasks(request)
	if err != nil {
		return
	}
	for _, row := range response.Response.Items {
		list = append(list, models.TaskDetail{
			Code:           *row.Code,
			Message:        *row.Message,
			JobId:          *row.JobId,
			Progress:       *row.Progress,
			TaskStatus:     *row.TaskStatus,
			TaskType:       *row.TaskType,
			StartTime:      *row.StartTime,
			EndTime:        *row.EndTime,
			AsyncRequestId: *row.AsyncRequestId,
		})
	}
	return
}

// QueryAsyncRequestDetail 查询异步请求详情
func (t *cdb) QueryAsyncRequestDetail(asyncId string) (result models.AsyncRequestInfoResponse, err error) {
	request := qcdb.NewDescribeAsyncRequestInfoRequest()
	request.AsyncRequestId = common.StringPtr(asyncId)
	response, err := t.client.DescribeAsyncRequestInfo(request)
	if err != nil {
		return
	}
	result.RequestId = response.Response.RequestId
	result.Status = response.Response.Status
	result.Info = response.Response.Info
	return
}

// ResetDBPassword 重置数据库实例用户密码
func (t *cdb) ResetDBPassword(id string, newPasswd string, user string, host string) (result models.CreateResponseParams, err error) {
	request := qcdb.NewModifyAccountPasswordRequest()
	request.InstanceId = common.StringPtr(id)
	request.NewPassword = common.StringPtr(newPasswd)
	request.Accounts = []*qcdb.Account{
		&qcdb.Account{
			User: common.StringPtr(user),
			Host: common.StringPtr(host),
		},
	}
	response, err := t.client.ModifyAccountPassword(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	result.TaskId = append(result.TaskId, *response.Response.AsyncRequestId)
	return
}

// ListDBUsers 获取数据库实例用户列表
func (t *cdb) ListDBUsers(id string) (list []models.DBAccountInfo, err error) {
	request := qcdb.NewDescribeAccountsRequest()
	request.InstanceId = common.StringPtr(id)
	request.Offset = common.Int64Ptr(0)
	request.Limit = common.Int64Ptr(100)
	response, err := t.client.DescribeAccounts(request)
	if err != nil {
		return
	}

	for _, row := range response.Response.Items {
		list = append(list, models.DBAccountInfo{
			Notes:              *row.Notes,
			Host:               *row.Host,
			User:               *row.User,
			ModifyTime:         *row.ModifyTime,
			ModifyPasswordTime: *row.ModifyPasswordTime,
			MaxUserConnections: *row.MaxUserConnections,
		})
	}
	return
}

// CreateDBUser 创建数据库实例用户
func (t *cdb) CreateDBUser(id string, args models.DBUserRequest) (err error) {
	request := qcdb.NewCreateAccountsRequest()
	request.InstanceId = common.StringPtr(id)
	request.Accounts = []*qcdb.Account{
		&qcdb.Account{
			User: common.StringPtr(args.User),
			Host: common.StringPtr(args.Host),
		},
	}
	request.Password = common.StringPtr(args.Password)
	request.Description = common.StringPtr(args.Description)
	request.MaxUserConnections = common.Int64Ptr(args.MaxUserConnections)
	_, err = t.client.CreateAccounts(request)
	return
}

// DeleteDBUer 删除数据库实例用户
func (t *cdb) DeleteDBUer(id string, user string, host string) (err error) {
	request := qcdb.NewDeleteAccountsRequest()
	request.InstanceId = common.StringPtr(id)
	request.Accounts = []*qcdb.Account{
		&qcdb.Account{
			User: common.StringPtr(user),
			Host: common.StringPtr(host),
		},
	}
	_, err = t.client.DeleteAccounts(request)
	return
}

// DisableWanAddr 关闭公网地址
func (t *cdb) DisableWanAddr(id string) (result models.CreateResponseParams, err error) {
	request := qcdb.NewCloseWanServiceRequest()
	request.InstanceId = common.StringPtr(id)
	response, err := t.client.CloseWanService(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	result.TaskId = append(result.TaskId, *response.Response.AsyncRequestId)
	return
}

// EnableWanAddr 开启公网地址
func (t *cdb) EnableWanAddr(id string) (result models.CreateResponseParams, err error) {
	request := qcdb.NewOpenWanServiceRequest()
	request.InstanceId = common.StringPtr(id)
	response, err := t.client.OpenWanService(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	result.TaskId = append(result.TaskId, *response.Response.AsyncRequestId)
	return
}

func (t *cdb) UpgradeEngineVersion() {

}

func (t *cdb) UpgradeConfigInfo(id string, args models.MySQLRequest) (result models.CreateResponseParams, err error) {
	request := qcdb.NewUpgradeDBInstanceRequest()
	request.InstanceId = common.StringPtr(id)
	if args.Memory >= 1000 {
		request.Memory = common.Int64Ptr(args.Memory)
	}
	if args.Cpu > 0 {
		request.Cpu = common.Int64Ptr(args.Cpu)
	}
	if args.Volume > 25 {
		request.Volume = common.Int64Ptr(args.Volume)
	}
	response, err := t.client.UpgradeDBInstance(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	for _, val := range response.Response.DealIds {
		result.TaskId = append(result.TaskId, *val)
	}
	return
}

// QueryInstanceSpecs 查询实例规格
func (t *cdb) QueryInstanceSpecs() {

}

// QueryPrepaidPrice 查询预付费价格。包年包月
func (t *cdb) QueryPrepaidPrice(args models.MySQLRequest) (price models.ItemPrice, err error) {
	return t.inquiryPrice("PRE_PAID", args)
}

// QueryPostpaidPrice 查询后付费价格，按时间计费
func (t *cdb) QueryPostpaidPrice(args models.MySQLRequest) (price models.ItemPrice, err error) {
	return t.inquiryPrice("HOUR_PAID", args)
}

// inquiryPrice 询价
func (t *cdb) inquiryPrice(payType string, args models.MySQLRequest) (price models.ItemPrice, err error) {
	request := qcdb.NewDescribeDBPriceRequest()
	request.Zone = common.StringPtr(args.Zone)
	request.GoodsNum = common.Int64Ptr(args.GoodsNum)
	request.Memory = common.Int64Ptr(args.Memory)
	request.Volume = common.Int64Ptr(args.Volume)
	request.PayType = common.StringPtr(payType)
	request.Period = common.Int64Ptr(args.Period)
	request.Cpu = common.Int64Ptr(args.Cpu)
	if payType == "PRE_PAID" {
		request.Period = common.Int64Ptr(args.Period)
	}
	if strings.TrimSpace(args.DeviceType) == "" {
		request.DeviceType = common.StringPtr("UNIVERSAL")
	} else {
		request.DeviceType = common.StringPtr(args.DeviceType)
	}
	response, err := t.client.DescribeDBPrice(request)
	if err != nil {
		return
	}
	res := response.Response
	if origin, err := strconv.ParseFloat(strconv.FormatInt(*res.Price, 10), 64); err == nil {
		price.OriginalPrice = origin
	}
	if discount, err := strconv.ParseFloat(strconv.FormatInt(*res.OriginalPrice, 10), 64); err == nil {
		price.DiscountPrice = discount
	}
	price.Currency = *res.Currency
	return
}

func (t *cdb) query(args map[string]string) (list []models.MySQLResponse, err error) {
	request := qcdb.NewDescribeDBInstancesRequest()
	if val, ok := args["id"]; ok {
		request.InstanceIds = common.StringPtrs([]string{val})
	}
	if val, ok := args["name"]; ok {
		request.InstanceNames = common.StringPtrs([]string{val})
	}
	response, err := t.client.DescribeDBInstances(request)
	if err != nil {
		return
	}
	for _, item := range response.Response.Items {
		list = append(list, models.MySQLResponse{
			WanStatus:     *item.WanStatus,
			Zone:          *item.Zone,
			InitFlag:      *item.InitFlag,
			Memory:        *item.Memory,
			Status:        *item.Status,
			VpcId:         *item.VpcId,
			InstanceId:    *item.InstanceId,
			Volume:        *item.Volume,
			AutoRenew:     *item.AutoRenew,
			ProtectMode:   *item.ProtectMode,
			SubnetId:      *item.SubnetId,
			InstanceType:  *item.InstanceType,
			Region:        *item.Region,
			DeadlineTime:  *item.DeadlineTime,
			DeployMode:    *item.DeployMode,
			TaskStatus:    *item.TaskStatus,
			DeviceType:    *item.DeviceType,
			EngineVersion: *item.EngineVersion,
			InstanceName:  *item.InstanceName,
			WanDomain:     *item.WanDomain,
			WanPort:       *item.WanPort,
			PayType:       *item.PayType,
			CreateTime:    *item.CreateTime,
			Vip:           *item.Vip,
			Vport:         *item.Vport,
			CdbError:      *item.CdbError,
			UniqVpcId:     *item.UniqVpcId,
			UniqSubnetId:  *item.UniqSubnetId,
			Cpu:           *item.Cpu,
			Qps:           *item.Qps,
			ZoneName:      *item.ZoneName,
			InstanceNodes: *item.InstanceNodes,
			DiskType:      *item.DiskType,
		})
	}
	return
}
